﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using MathNet.Numerics.LinearAlgebra;

using cit.CopyDetection.Model;
using cit.CopyDetection.DataObject;

namespace cit.CopyDetection.DatabaseAccess
{
    /// <summary>
    /// Đọc và ghi dữ liệu ra tập tin XML
    /// </summary>
    public class ModelWriter
    {
        /// <summary>
        /// Ghi tập tự vựng ra file XML
        /// </summary>
        /// <param name="path">Địa chỉ ghi file</param>
        /// <param name="vocabulary">Tập từ vựng cần ghi file</param>
        public static void WriteVocabulary(string path, List<Term> vocabulary)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;

            using (XmlWriter writer = XmlWriter.Create(path, settings))
            {
                writer.WriteStartElement("Vocabulary");
                writer.WriteAttributeString("nterms", vocabulary.Count.ToString());
                foreach (Term term in vocabulary)
                {
                    writer.WriteStartElement("Term");
                    writer.WriteAttributeString("parafreq", term.ParagraphFrequency.ToString());
                    writer.WriteAttributeString("globalfreq", term.GlobalFrequency.ToString());
                    writer.WriteString(term.WordString);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
        }

        /// <summary>
        /// Ghi mô hình vector ra file XML
        /// </summary>
        /// <param name="path">Đường dẫn file</param>
        /// <param name="localWtFunc">Hàm đánh trọng số từ cục bộ sử dụng</param>
        /// <param name="globalWtFunc">Hàm đánh trọng số từ toàn cục sử dụng</param>
        /// <param name="paraModels">Thông tin và các vector đoạn văn bản</param>
        /// <param name="positions">Vị trí các tài liệu trong ma trận</param>
        public static void WriteModel(
            string path, 
            string localWtFunc, string globalWtFunc, 
            List<ParagraphModel> paraModels, Dictionary<Document, Position> positions) 
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;            
            
            Matrix matrix = MatrixExtension.CreateMatrix(paraModels);
            SingularValueDecomposition SVD = matrix.SingularValueDecomposition;
            Matrix leftSingularVectors = SVD.LeftSingularVectors;
            Matrix rightSingularVectors = SVD.RightSingularVectors;
            Vector singularValues = SVD.SingularValues;

            using (XmlWriter writer = XmlWriter.Create(path, settings))
            {
                writer.WriteStartElement("Model");

                // Write weighting fuctions
                writer.WriteStartElement("WtScheme");
                writer.WriteElementString("LocalWtFunc", localWtFunc);
                writer.WriteElementString("GlobalWtFunc", globalWtFunc);
                writer.WriteEndElement();

                // Write documents
                int ndocuments = positions.Count;
                writer.WriteStartElement("Documents");
                writer.WriteAttributeString("ndocuments", ndocuments.ToString());
                foreach (KeyValuePair<Document, Position> position in positions)
                {
                    Document document = position.Key;
                    Position pos = position.Value;

                    writer.WriteStartElement("Document");
                    writer.WriteAttributeString("documentid", document.Name);
                    writer.WriteAttributeString("location", document.Location);
                    writer.WriteAttributeString("begin", pos.Begin.ToString());
                    writer.WriteAttributeString("end", pos.End.ToString());
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();

                // Write matrix and vector
                WriteMatrix("LeftSingularVectors", leftSingularVectors, writer);
                WriteMatrix("RightSingularVectors", rightSingularVectors, writer);
                WriteVector("SingularValues", singularValues, writer);

                writer.WriteEndElement();
            }
        }

        /// <summary>
        /// Ghi ma trận ra file XML
        /// </summary>
        /// <param name="name">Tên nút bắt đầu</param>
        /// <param name="matrix">Ma trận cần ghi ra file</param>
        /// <param name="writer">Đối tượng XMLWriter liên kết với file</param>
        private static void WriteMatrix(string name, Matrix matrix, XmlWriter writer)
        {
            int columns = matrix.ColumnCount;
            int rows = matrix.RowCount;

            writer.WriteStartElement(name);

            writer.WriteStartElement("Columns");
            writer.WriteValue(columns);
            writer.WriteEndElement();

            writer.WriteStartElement("Rows");
            writer.WriteValue(rows);
            writer.WriteEndElement();

            writer.WriteStartElement("Elements");
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < columns; j++)
                {
                    writer.WriteStartElement("Element");
                    writer.WriteValue(matrix[i, j]);
                    writer.WriteEndElement();
                }
            }
            writer.WriteEndElement();

            writer.WriteEndElement();
        }

        /// <summary>
        /// Ghi vector ra file XML
        /// </summary>
        /// <param name="name">Tên nút bắt đầu</param>
        /// <param name="vector">Vector cần ghi ra file</param>
        /// <param name="writer">Đối tượng XMLWriter liên kết với file XML</param>
        private static void WriteVector(string name, Vector vector, XmlWriter writer)
        {
            int dim = vector.Length;

            writer.WriteStartElement(name);

            writer.WriteStartElement("Dimension");
            writer.WriteValue(dim);
            writer.WriteEndElement();

            writer.WriteStartElement("Elements");
            for (int i = 0; i < dim; i++)
            {
                writer.WriteStartElement("Element");
                writer.WriteValue(vector[i]);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
}
